package com.sinbad.interview.spring.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
import java.util.Map;


/**
 * 基于 Redis Bitmap 的签到系统完整实现
 * <p>
 * 用户签到接口：/sign
 * 查询用户是否签到：/signed
 * 查询用户当月已签到天数：/count
 * 查询用户当月签到情况列表：/signlog
 * 我们用 Bitmap 每位代表一天，偏移量就是日期（day - 1），例如 4 月 15 日 = 偏移量 14。
 *
 * @author sinbad.cheng
 */
@Service
public class SignService {

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    private String buildKey(Long userId, LocalDate date) {
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMM");
        return "user:sign:" + userId + ":" + date.format(formatter);
    }

    /**
     * 用户签到接口
     * <p>
     * 空间极小：每月 31 天只占用 4 字节（31 bit）
     * 查询高效：直接位操作，O(1)
     * 结构清晰：一个 key 表示一个月的签到记录
     */
    public boolean doSign(Long userId) {
        LocalDate today = LocalDate.now().minusDays(1);
        int dayOffset = today.getDayOfMonth() - 1;
        String key = buildKey(userId, today);

        Boolean signed = redisTemplate.opsForValue().getBit(key, dayOffset);
        if (Boolean.TRUE.equals(signed)) {
            return false;
        }

        redisTemplate.opsForValue().setBit(key, dayOffset, true);
        return true;
    }

    public boolean checkSign(Long userId, int dayOfMonth) {
        if (dayOfMonth < 1 || dayOfMonth > 31) return false;

        LocalDate today = LocalDate.now();
        String key = buildKey(userId, today);
        return Boolean.TRUE.equals(redisTemplate.opsForValue().getBit(key, dayOfMonth - 1));
    }

    public long countSignedDays(Long userId) {
        LocalDate today = LocalDate.now();
        String key = buildKey(userId, today);
        return redisTemplate.execute((RedisCallback<Long>) connection -> connection.bitCount(redisTemplate.getStringSerializer().serialize(key)));
    }


    public Map<Integer, Boolean> getSignLog(Long userId) {
        LocalDate today = LocalDate.now();
        int daysInMonth = today.lengthOfMonth();
        String key = buildKey(userId, today);
        Map<Integer, Boolean> result = new HashMap<>();

        for (int i = 0; i < daysInMonth; i++) {
            Boolean signed = redisTemplate.opsForValue().getBit(key, i);
            result.put(i + 1, Boolean.TRUE.equals(signed));
        }
        return result;
    }
}
